package com.edu.internship.util;

import com.edu.internship.glabal.constants.ResultCodeEnum;
import com.edu.internship.glabal.exception.BusinessException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

/**
 * code is far away from bug with the animal protecting
 *
 * @author lishiquan
 * @description
 * @date 2021-07-14
 */
@Slf4j
public class JsonUtils {

    /**
     * 默认日期格式
     */
    private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    /**
     * 默认时间格式
     */
    private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    private static final JsonUtils ME = new JsonUtils();

    public static synchronized JsonUtils getInstance() {
        return ME;
    }

    private static final ObjectMapper OBJECT_MAPPER;

    private JsonUtils() {
    }

    static {
        OBJECT_MAPPER = new ObjectMapper();
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        OBJECT_MAPPER.setDateFormat(new StdDateFormat());
        OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        OBJECT_MAPPER.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        OBJECT_MAPPER.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer());

        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer());

        OBJECT_MAPPER.registerModule(javaTimeModule)
                .registerModule(new ParameterNamesModule());
    }

    public ObjectMapper getObjectMapper() {
        return OBJECT_MAPPER;
    }

    public <T> String convertToStr(T obj) {
        try {
            return getObjectMapper().writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("转换对象 -> JSON字符串异常: {}", e.getMessage());
            throw new BusinessException(ResultCodeEnum.CONVERT_ERROR, "转换对象 -> JSON字符串异常");
        }
    }

    public <T> byte[] convertToBytes(T obj) {
        try {
            return getObjectMapper().writeValueAsBytes(obj);
        } catch (JsonProcessingException e) {
            log.error("转换对象 -> JSON字符串异常: {}", e.getMessage());
            throw new BusinessException(ResultCodeEnum.CONVERT_ERROR, "转换对象 -> JSON字符串异常");
        }
    }

    public <K, V> Map<String, V> convertToMap(String json, Class<K> kType, Class<V> vType) {
        try {
            ObjectMapper objectMapper = getObjectMapper();
            MapType mapType = objectMapper.getTypeFactory().constructMapType(Map.class, kType, vType);
            return objectMapper.readValue(json, mapType);
        } catch (IOException e) {
            log.error("转换JSON字符串 -> 数组异常: {}", e.getMessage());
            throw new BusinessException(ResultCodeEnum.CONVERT_ERROR, "转换JSON字符串 -> 数组<T>异常");
        }
    }

    public <T> T convertToBean(Object obj, Class<T> clazz) {
        return getObjectMapper().convertValue(obj, clazz);
    }

    public <T> List<T> convertToBeanList(Object obj) {
        return getObjectMapper().convertValue(obj, new TypeReference<List<T>>() {
        });
    }

    public <T> T convertToObj(String json, Class<T> clazz) {
        try {
            return getObjectMapper().readValue(json, clazz);
        } catch (JsonProcessingException e) {
            log.error("转换JSON字符串 -> 对象异常: {}", e.getMessage());
            throw new BusinessException(ResultCodeEnum.CONVERT_ERROR, "转换JSON字符串 -> 对象异常");
        }
    }

    public <T> T convertToObj(byte[] bytes, Class<T> clazz) {
        try {
            return getObjectMapper().readValue(bytes, clazz);
        } catch (IOException e) {
            log.error("转换JSONByte -> 对象异常: {}", e.getMessage());
            throw new BusinessException(ResultCodeEnum.CONVERT_ERROR, "转换JSONByte -> 对象异常");
        }
    }

    public <T> List<T> convertToList(String json, Class<T> cls) {
        try {
            ObjectMapper objectMapper = getObjectMapper();
            CollectionType listType = objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, cls);
            return objectMapper.readValue(json, listType);
        } catch (JsonProcessingException e) {
            log.error("转换JSON字符串 -> 数组异常: {}", e.getMessage());
            throw new BusinessException(ResultCodeEnum.CONVERT_ERROR, "转换JSON字符串 -> 数组<T>异常");
        }
    }

    public <T> List<T> convertToListMap(String json) {
        try {
            ObjectMapper objectMapper = getObjectMapper();
            CollectionType listType = objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, Map.class);
            return objectMapper.readValue(json, listType);
        } catch (JsonProcessingException e) {
            log.error("转换JSON字符串 -> 数组<Map>异常: {}", e.getMessage());
            throw new BusinessException(ResultCodeEnum.CONVERT_ERROR, "转换JSON字符串 -> 数组<Map>异常");
        }
    }

    public <T> List<Map<String, Object>> listBeanToMap(List<T> list) {
        return getObjectMapper().convertValue(list, new TypeReference<List<Map<String, Object>>>() {
        });
    }

    public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                throws IOException {
            if (localDateTime != null) {
                jsonGenerator.writeNumber(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
            }
        }
    }

    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
                throws IOException {
            long timestamp = jsonParser.getValueAsLong();
            if (timestamp > 0) {
                return Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
            } else {
                return null;
            }
        }
    }

    public static class LocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                throws IOException {
            if (localDate != null) {
                jsonGenerator.writeString(localDate.toString());
            }
        }
    }

    public static class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
        @Override
        public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
                throws IOException {
            String localDateStr = jsonParser.getValueAsString();
            if (Tools.isNotBlank(localDateStr)) {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
                return DateUtils.convertStringToLocalDate(localDateStr, formatter);
            } else {
                return null;
            }
        }
    }

    public static class LocalTimeSerializer extends JsonSerializer<LocalTime> {
        @Override
        public void serialize(LocalTime localTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                throws IOException {
            if (localTime != null) {
                jsonGenerator.writeString(localTime.toString());
            }
        }
    }

    public static class LocalTimeDeserializer extends JsonDeserializer<LocalTime> {
        @Override
        public LocalTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
                throws IOException {
            String localTimeStr = jsonParser.getValueAsString();
            if (Tools.isNotBlank(localTimeStr)) {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT);
                return DateUtils.convertStringToLocalTime(localTimeStr, formatter);
            } else {
                return null;
            }
        }
    }
}
